<?php declare(strict_types=1);
/**
 * @author      xianganyall <xianganyall@gmail.com>
 * @copyright   2023-2025 owner
 **/

namespace Srv\Apps\Apis\Common\IdxCtl;

use Srv\Libs\Common\CommNet;
use Srv\Libs\Common\CommString;
use Srv\Libs\Common\CommValid;
use Srv\Libs\Frame\ControllerAbstract;
use Srv\Libs\Storage\DataMysql;
use Srv\Libs\Tools\IpAddr;

final class IdxIp extends ControllerAbstract
{
    private ?DataMysql $DbAuto              = null;         // DB-Write
    private string $tableName               = 'idx_ipv6';
    private string $fieldId                 = 'ipv6_id';
    private string $fieldOne                = 'ipv6_one';
    private string $fieldTwo                = 'ipv6_two';
    private string $fieldThree              = 'ipv6_three';
    private string $fieldFour               = 'ipv6_four';
    private string $fieldFive               = 'ipv6_five';
    private string $fieldSix                = 'ipv6_six';
    private string $fieldSeven              = 'ipv6_seven';
    private string $fieldEight              = 'ipv6_eight';

    /**
     * @return static
     * tag
     */
    public static function tag():self
    {
        $thisObject                         = self::tagGet();
        if($thisObject instanceof self){
            $thisObject->DbAuto             = self::getDbAuto();
        }
        return $thisObject;
    }

    /**
     * @param string $ipIntStr
     * @param array $ipv6Num8List
     * @return void
     * addIpv6Data
     */
    private function addIpv6Data(string $ipIntStr, array $ipv6Num8List):void
    {
        if(strlen($ipIntStr) < 1 || !CommValid::isNumber($ipIntStr) || count($ipv6Num8List) !== 8) return;
        $ipv6Num8One    = $ipv6Num8List[0]??0;
        $ipv6Num8Two    = $ipv6Num8List[1]??0;
        $ipv6Num8Three  = $ipv6Num8List[2]??0;
        $ipv6Num8Four   = $ipv6Num8List[3]??0;
        $ipv6Num8Five   = $ipv6Num8List[4]??0;
        $ipv6Num8Six    = $ipv6Num8List[5]??0;
        $ipv6Num8Seven  = $ipv6Num8List[6]??0;
        $ipv6Num8Eight  = $ipv6Num8List[7]??0;
        $this->DbAuto->getDataId('INSERT INTO '.$this->DbAuto->getTableNameAll($this->tableName).' SET `'.$this->fieldId.'`='.$ipIntStr.', `'.$this->fieldOne.'`='.$ipv6Num8One.', `'.$this->fieldTwo.'`='.$ipv6Num8Two.', `'.$this->fieldThree.'`='.$ipv6Num8Three.', `'.$this->fieldFour.'`='.$ipv6Num8Four.', `'.$this->fieldFive.'`='.$ipv6Num8Five.', `'.$this->fieldSix.'`='.$ipv6Num8Six.', `'.$this->fieldSeven.'`='.$ipv6Num8Seven.', `'.$this->fieldEight.'`='.$ipv6Num8Eight);
    }

    /**
     * @param string $ipIntStr
     * @param bool $isFill
     * @param bool $isExists
     * @return string
     * getIpv6StrByIntStr
     */
    private function getIpv6StrByIntStr(string $ipIntStr, bool $isFill = false, bool &$isExists = false):string
    {
        $isExists       = false;
        if(strlen($ipIntStr) < 1 || !CommValid::isNumber($ipIntStr)) return '';
        $isFail         = false;
        $valData        = $this->DbAuto->getDataOne('SELECT `'.$this->fieldOne.'` AS _one, `'.$this->fieldTwo.'` AS _two, `'.$this->fieldThree.'` AS _three, `'.$this->fieldFour.'` AS _four, `'.$this->fieldFive.'` AS _five, `'.$this->fieldSix.'` AS _six, `'.$this->fieldSeven.'` AS _seven, `'.$this->fieldEight.'` AS _eight FROM '.$this->DbAuto->getTableNameAll($this->tableName).' WHERE `'.$this->fieldId.'`='.$ipIntStr, $isFail);
        if($isFail || !isset($valData['_one'])) return '';
        $isExists       = true;
        $ipv6Num8List   = [intval($valData['_one']), intval($valData['_two']), intval($valData['_three']), intval($valData['_four']), intval($valData['_five']), intval($valData['_six']), intval($valData['_seven']), intval($valData['_eight'])];
        return CommNet::num8ToIpv6($ipv6Num8List, $isFill);
    }

    /**
     * @param int $ipInt
     * @param bool $isFill
     * @param bool $isExists
     * @param bool $isIpv6
     * @return string
     * getIpStrByInt
     */
    public function getIpStrByInt(int $ipInt, bool $isFill = false, bool &$isExists = false, bool &$isIpv6 = false):string
    {
        if($ipInt >= 0 && $ipInt <= 4294967295){
            $isExists   = true;
            return CommNet::longToIp($ipInt);
        }
        $ipv6Str        = $this->getIpv6StrByIntStr(CommString::int64ToUnsignedStr($ipInt), $isFill, $isExists);
        if(strlen($ipv6Str) > 0) $isIpv6 = true;
        return $ipv6Str;
    }

    /**
     * @param string $ipIntStr
     * @param bool $isFill
     * @param bool $isExists
     * @param bool $isIpv6
     * @return string
     * getIpStrByIntStr
     */
    public function getIpStrByIntStr(string $ipIntStr, bool $isFill = false, bool &$isExists = false, bool &$isIpv6 = false):string
    {
        if(bccomp($ipIntStr, '4294967295') !== 1 && bccomp($ipIntStr, '0') !== -1){
            $isExists   = true;
            return CommNet::longToIp(intval($ipIntStr));
        }
        $ipv6Str        = $this->getIpv6StrByIntStr($ipIntStr, $isFill, $isExists);
        if(strlen($ipv6Str) > 0) $isIpv6 = true;
        return $ipv6Str;
    }

    /**
     * @param string $ipStr
     * @param bool $ipv6Allow
     * @param array $ipv6Num8List
     * @return string
     * genIpNumStr
     */
    public function genIpNumStr(string $ipStr, bool $ipv6Allow = true, array &$ipv6Num8List = []):string
    {
        $ipIntStr           = CommNet::genIpNumStr($ipStr, $ipv6Allow, $ipv6Num8List);
        if(bccomp($ipIntStr, '4294967295') === 1) $this->addIpv6Data($ipIntStr, $ipv6Num8List); // IPv6
        return $ipIntStr;
    }

    /**
     * @param array $beforeHeadKeyList
     * @param int $forwardForType
     * @param bool $ipv6Allow
     * @param string $ipStr
     * @param array $ipv6Num8List
     * @return string
     * getReqIpNumStr
     */
    public function getReqIpNumStr(array $beforeHeadKeyList = [], int $forwardForType = CommNet::REQ_FORWARD_FOR_TYPE_BEFORE, bool $ipv6Allow = true, string &$ipStr = '', array &$ipv6Num8List = []):string
    {
        $ipIntStr           = CommNet::getReqIpNumStr($beforeHeadKeyList, $forwardForType, $ipv6Allow, $ipStr, $ipv6Num8List);
        if(bccomp($ipIntStr, '4294967295') === 1) $this->addIpv6Data($ipIntStr, $ipv6Num8List); // IPv6
        return $ipIntStr;
    }

    /**
     * @param string $ipStr
     * @return int
     * getGlobalCodeByIpLong
     */
    public function getGlobalCodeByIpLong(string $ipStr):int
    {
        if(strlen($ipStr) < 7 || $ipStr === '0.0.0.0' || !CommValid::isIp($ipStr)) return 1;
        $IpAddr         = new IpAddr();
        $location       = $IpAddr->getLocationAnalyze($ipStr);
        if(!isset($location['analyze']) || !is_int($location['analyze']['countryNum']) || $location['analyze']['countryNum'] < 1 || $location['analyze']['countryNum'] > 999) return 1;
        $countryNum     = $location['analyze']['countryNum']??0;
        // china
        if($countryNum === 156){
            $parentCode         = $location['analyze']['provinceCode']??'';
            if(strlen($location['analyze']['cityCode']) < 1 && strlen($location['analyze']['city']) > 0){
                $sqlList        = [];
                if(strlen($parentCode) > 0){
                    $sqlList[]  = 'SELECT dacn_code FROM '.$this->DbAuto->getTableNameAll('def_area_chn').' WHERE dacn_code_parent=\''.$this->DbAuto->getRealEscapeString($parentCode).'\' AND dacn_name=\''.$this->DbAuto->getRealEscapeString($location['analyze']['city']).'\'';
                    $sqlList[]  = 'SELECT dacn_code FROM '.$this->DbAuto->getTableNameAll('def_area_chn').' WHERE dacn_code_parent=\''.$this->DbAuto->getRealEscapeString($parentCode).'\' AND dacn_name LIKE \''.$this->DbAuto->getRealEscapeString($location['analyze']['city']).'%\'';
                    $sqlList[]  = 'SELECT dacn_code FROM '.$this->DbAuto->getTableNameAll('def_area_chn').' WHERE dacn_code_parent=\''.$this->DbAuto->getRealEscapeString($parentCode).'\' AND dacn_name LIKE \'%'.$this->DbAuto->getRealEscapeString($location['analyze']['city']).'%\'';
                }else{
                    $sqlList[]  = 'SELECT dacn_code, dacn_code_parent FROM '.$this->DbAuto->getTableNameAll('def_area_chn').' WHERE dacn_name=\''.$this->DbAuto->getRealEscapeString($location['analyze']['city']).'\'';
                    $sqlList[]  = 'SELECT dacn_code, dacn_code_parent FROM '.$this->DbAuto->getTableNameAll('def_area_chn').' WHERE dacn_name LIKE \''.$this->DbAuto->getRealEscapeString($location['analyze']['city']).'%\'';
                    $sqlList[]  = 'SELECT dacn_code, dacn_code_parent FROM '.$this->DbAuto->getTableNameAll('def_area_chn').' WHERE dacn_name LIKE \'%'.$this->DbAuto->getRealEscapeString($location['analyze']['city']).'%\'';
                }
                foreach ($sqlList as $sql){
                    $codeInfo   = $this->DbAuto->getDataOne($sql);
                    if(isset($codeInfo['dacn_code'])){
                        $location['analyze']['cityCode'] = $codeInfo['dacn_code'];
                        if(strlen($location['analyze']['provinceCode']) < 1 && isset($codeInfo['dacn_code_parent'])) $location['analyze']['provinceCode'] = $codeInfo['dacn_code_parent'];
                        break;
                    }
                }
            }
        }
        $provinceCode   = $location['analyze']['provinceCode']??'';
        $cityCode       = $location['analyze']['cityCode']??'';
        $codeList       = [];
        if(strlen($cityCode) === 6 && preg_match('/^\d{6}$/', $cityCode)) $codeList[] = intval((1000+$countryNum).$cityCode);
        if(strlen($provinceCode) === 6 && preg_match('/^\d{6}$/', $provinceCode)) $codeList[] = intval((1000+$countryNum).$provinceCode);
        $codeList[]     = intval((1000+$countryNum).'000000');
        $codeRes        = $this->DbAuto->getDataInt('SELECT dall_code FROM '.$this->DbAuto->getTableNameAll('def_area_all').' WHERE dall_code IN ('.implode(',', $codeList).') ORDER BY dall_code DESC', 'dall_code');
        return max($codeRes, 1);
    }
}